Generator
Generator函数算是一个状态机,或者说他是一个具有Iterator接口特性的函数。
1 2 3 4 5 6 7
| function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();
|
Generator函数与普通函数之间的区别有两点:
1:函数名前有个*
2:yield表达式
yield表达式
yield表达式是产出的意思,配合下面这块代码更好的理解yield表达式。
1 2 3 4 5 6 7 8 9 10 11
| hw.next() hw.next() hw.next() hw.next()
|
上面这块代码中,我们执行hw函数的next()方法,(实际是Iterator接口提供的next方法)使得该Generator输出yield定义的值
也就是说,在Generator中,每次执行next(),都会执行到yield表达式为止。
1 2 3 4 5
| function* tezml() { console.log("tezml") } let name = tezml(); name.next();
|
在Generator中,如果没有yield表达式,Generator就变成了一个普通函数,只是从直接调用变成 .next()调用。
1 2 3 4
| function tezml(){ yield 1; }
|
另外yield表达式只能用在Generator中,用在普通函数中会报错
1 2 3 4 5 6 7
| function* demo() { console.log('Hello' + yield); console.log('Hello' + yield 123); console.log('Hello' + (yield)); console.log('Hello' + (yield 123)); }
|
yield表达式如果用在另一个表达式之中,必须放在圆括号里面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() a.next() a.next() var b = foo(5); b.next() b.next(12) b.next(13)
|
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
Generator的遍历
如果只用next当然很不智能了,由于Generator函数具有Iterator特性,所以我们可以使用for…of来完成遍历
1 2 3 4 5 6 7 8 9 10 11 12 13
| function *foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); }
|
1 2 3 4 5 6 7 8 9 10 11
| function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() g.return('foo') g.next()
|
Generator有个return方法来结束当前遍历,并接受一个参数来代替结束值
yield* 表达式
Generator既然是函数就避免不了调用,当我们涉及到Generator调用Generator时候就会用到yield*表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function* foo() { yield 'a'; yield 'b'; } function* bar() { yield 'x'; foo(); yield 'y'; } for (let v of bar()){ console.log(v); }
|
上面这块代码是调用不起来foo函数的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| function* bar() { yield 'x'; yield* foo(); yield 'y'; } function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); }
|
async
async可以理解为是Generator的语法糖。用处就是处理异步操作,其用法就是把Generator中的’*’换成了’async’,把yield替换成了await
1 2 3 4 5 6
| var asyncReadFile = async function () { var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
|
async函数对 Generator 函数的有一些改进,具体在:
1、内置执行器
Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
2、返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
基本用法
1 2 3 4 5 6 7 8 9
| async function getStockPriceByName(name) { var symbol = await getStockSymbol(name); var stockPrice = await getStockPrice(symbol); return stockPrice; } getStockPriceByName('goog').then(function (result) { console.log(result); });
|
一些注意点
await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } async function myFunction() { await somethingThatReturnsAPromise() .catch(function (err) { console.log(err); }); }
|
await命令只能用在async函数之中,如果用在普通函数,就会报错。
1 2 3 4 5 6 7 8
| async function dbFuc(db) { let docs = [{}, {}, {}]; docs.forEach(function (doc) { await db.post(doc); }); }
|
async 函数的实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
1 2 3 4 5 6 7 8 9 10 11
| async function fn(args) { } function fn(args) { return spawn(function* () { }); }
|